package jp.tkym.labs.store.sql;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import jp.tkym.labs.store.BeanstoreException;
import jp.tkym.labs.store.query.BeanQuery;
import jp.tkym.labs.store.query.BeanQueryFilter;
import jp.tkym.labs.store.query.BeanQueryFilterOperator;
import jp.tkym.labs.store.query.BeanQuerySorter;
import jp.tkym.labs.util.beankey.BeanKey;

public class SqliteBeanQuery<B> implements BeanQuery<B>{
	private List<BeanQueryFilter> filterList = new ArrayList<BeanQueryFilter>();
	private List<BeanQuerySorter> sorterList = new ArrayList<BeanQuerySorter>();
	private SqliteCriteriaInterpreter<B> interpreter;
	private PreparedStatementExecutor executor;
	private Class<B> cls;
	
	public SqliteBeanQuery(Class<B> cls, PreparedStatementExecutor executor){
		this.cls = cls;
		interpreter = new SqliteCriteriaInterpreter<B>(cls);
		this.executor = executor;
	}
	
	@Override
	public BeanQuery<B> filter(BeanQueryFilter... filters){
		for(BeanQueryFilter filter : filters)filterList.add(filter);
		return this;
	}
	
	@Override
	public BeanQuery<B> sort(BeanQuerySorter... sorters) {
		for(BeanQuerySorter sorter : sorters)sorterList.add(sorter);
		return this;
	}
	
	private String appendSql() throws BeanstoreException{
		try {
			String appendSql =
				interpreter.whereStatement(filterList) +
				interpreter.orderByStatement(sorterList);
			return appendSql;
		} catch (IllegalArgumentException e) {
			throw new BeanstoreException(e);
		}
	}
	
	private Object[] filterValues(){
		Object[] values = new Object[filterList.size()];
		for(int i=0; i<filterList.size(); i++){
			values[i] = filterValue(filterList.get(i));
		}
		return values;
	}
	
	private Object filterValue(BeanQueryFilter filter){
		BeanQueryFilterOperator operator = filter.getFilterOperator();
		if(operator.equals(BeanQueryFilterOperator.START_WITH)) 
			return filter.getValue()+"%";
		if(operator.equals(BeanQueryFilterOperator.END_WITH)) 
			return "%"+filter.getValue();
		if(operator.equals(BeanQueryFilterOperator.CONTAIN)) 
			return "%"+filter.getValue()+"%";
		return filter.getValue();
	}
	
	@Override
	public Iterator<B> asIterator() throws BeanstoreException{
		try {
			return executeEntityQuery().asIterator();
		} catch (SQLException e) {
			throw new BeanstoreException(e);
		}
	}
		
	
	@Override
	public List<B> asList() throws BeanstoreException{
		try {
			return executeEntityQuery().asList();
		} catch (SQLException e) {
			throw new BeanstoreException(e);
		}
	}
	
	private PreparedStatementQuery<B> executeEntityQuery() throws BeanstoreException{
		try {
			PreparedStatementQuery<B> query = executor.nativeEntityQuery(cls, appendSql(), filterValues());
			query.execute();
			return query;
		} catch (SQLException e) {
			throw new BeanstoreException(e);
		}
	}
	
	@Override
	public Set<BeanKey> keySet() throws BeanstoreException {
		try {
			return executeKeyQuery().asKeySet();
		} catch (SQLException e) {
			throw new BeanstoreException(e);
		}
	}
	
	private PreparedStatementQuery<B> executeKeyQuery() throws BeanstoreException{
		try {
			PreparedStatementQuery<B> query = executor.nativeKeyQuery(cls, appendSql(), filterValues());
			query.execute();
			return query;
		} catch (SQLException e) {
			throw new BeanstoreException(e);
		}
	}
}